package com.itao.springboot.aop;

import com.itao.springboot.annotation.RateLimiter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * 限流的切面
 *
 * @author sjt
 * @Date 2025/3/10 9:23
 */
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class RateLimiterAspect {

    /**
     * 用于获取方法参数定义名字.
     */
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    /**
     * 用于SpEL表达式解析.
     */
    private static final SpelExpressionParser parser = new SpelExpressionParser();


    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.itao.springboot.annotation.RateLimiter)")
    public void rateLimiter() {
    }

    @Around("rateLimiter()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取目标类
        Object target = joinPoint.getThis();
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
        // 获取方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 可解决代理对象问题
        Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(target, targetMethod, args, parameterNameDiscoverer);
        // 处理注解上的值
        String mes = rateLimiter.msg();
        String key = rateLimiter.key();
        String prefixKey = "rateLimiter" + ":" + target.getClass().getName() + "#" + method.getName();
        String redisKey;
        if (key == null || key.isEmpty()) {
            redisKey = prefixKey + ":" + getUserId();
        } else {
            redisKey = prefixKey + ":" + getExpValue(context, key);
        }
        int count = rateLimiter.count();
        int time = rateLimiter.time();

        RRateLimiter rRateLimiter = redissonClient.getRateLimiter(redisKey);

        // 初始化 RateLimiter 的状态
        rRateLimiter.trySetRate(RateType.OVERALL, count, time, RateIntervalUnit.SECONDS);

        // 设置过期时间
        rRateLimiter.expire(Duration.ofSeconds(time + 1));

        boolean tryAcquire = rRateLimiter.tryAcquire();
        if (!tryAcquire) {
            throw new RuntimeException(mes);
        }
        return joinPoint.proceed();
    }

    private String getExpValue(MethodBasedEvaluationContext context, String expr) {
        Expression expression = parser.parseExpression(expr);
        return String.valueOf(expression.getValue(context));
    }


    private String getUserId() {
        return "12345";
    }
}
